package com.hptpd.questionnairesurvey.service.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hptpd.questionnairesurvey.assets.Constant;
import com.hptpd.questionnairesurvey.component.PageResult;
import com.hptpd.questionnairesurvey.component.Result;
import com.hptpd.questionnairesurvey.domain.*;
import com.hptpd.questionnairesurvey.domain.question.BaseQuestion;
import com.hptpd.questionnairesurvey.repository.*;
import com.hptpd.questionnairesurvey.service.ISurveyService;
import com.hptpd.questionnairesurvey.service.base.BaseServiceImpl;
import com.hptpd.questionnairesurvey.utils.AbstractMyBeanUtils;
import com.hptpd.questionnairesurvey.utils.ExcelUtil;
import com.hptpd.questionnairesurvey.vo.*;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.*;

/**
 * \* Created with IntelliJ IDEA.
 * \* Date: 2019/10/23
 * \*
 * \* Description:
 * \
 *
 * @author walter_long
 */
@Service("iSurveyService")
public class SurveyServiceImpl extends BaseServiceImpl implements ISurveyService {
    private Logger logger = LoggerFactory.getLogger(SurveyServiceImpl.class);

    @Resource(name = "surveyRep")
    private SurveyRep surveyRep;
    @Resource(name = "questionnaireRep")
    private QuestionnaireRep questionnaireRep;
    @Resource(name = "userRep")
    private UserRep userRep;
    @Resource(name = "userSurveyRep")
    private UserSurveyRep userSurveyRep;
    @Resource(name = "baseQuestionRep")
    private BaseQuestionRep baseQuestionRep;
    @Resource(name = "userSurveyResultRep")
    private UserSurveyResultRep userSurveyResultRep;
    @Resource(name = "dimensionalityRep")
    private DimensionalityRep dimensionalityRep;
    @Resource(name = "choiceRep")
    private ChoiceRep choiceRep;
    @Resource(name = "userSurveyScoreRep")
    private UserSurveyScoreRep userSurveyScoreRep;
    @Resource
    private FillsUpRep fillsUpRep;
    @Resource
    private SingleChoiceRep singleChoiceRep;

    @Override
    public Result listSurveysByPage(@NotNull Map<String, Object> paramMap, @NotNull Pageable pageable) {
        logger.debug("listSurveysByPage paramMap: {}", paramMap);
        logger.debug("listSurveysByPage pageable: {}", pageable);

        Page<Survey> page = surveyRep.findByNameContainsOrderByCreatedAtDesc((String)paramMap.get("name"), pageable);
        return Result.ok(PageResult.setPageResult(pageable, page.getTotalElements(), SurveyVo.convertSurvey(page.getContent())));
    }

    @Override
    public Result listWxSurveysByPage(@NotNull Map<String, Object> paramMap, @NotNull Pageable pageable) {
        logger.debug("listWxSurveysByPage paramMap: {}", paramMap);
        logger.debug("listWxSurveysByPage pageable: {}", pageable);
        Page<UserSurvey> page;
        Boolean isDone = (Boolean)paramMap.get("isDone");
        if (isDone) {
            page = userSurveyRep.findByIsDoneAndUser_IdIsOrderBySurvey_CreatedAtDesc((Boolean)paramMap.get("isDone"), (String)paramMap.get("userId"), pageable);
        } else {
            page = userSurveyRep.findByIsDoneAndUser_IdIsAndSurvey_StateIsOrderBySurvey_CreatedAtDesc((Boolean)paramMap.get("isDone"), (String)paramMap.get("userId"), Constant.SURVEY_ING, pageable);
        }
        return Result.ok(PageResult.setPageResult(pageable, page.getTotalElements(), UserSurveyVo.convertUserSurvey(page.getContent())));
    }

    @Override
    public Result restartSurvey(@NotNull SurveyVo surveyVo) {
        logger.debug("restartSurvey surveyVo: {}", surveyVo);

        Optional<Survey> surveyOptional = surveyRep.findById(surveyVo.getId());
        if (!surveyOptional.isPresent()) {
            return Result.badArgumentValue();
        }
        Survey survey = surveyOptional.get();
        survey.setState(Constant.SURVEY_ING);
        surveyRep.save(survey);
        return Result.ok();
    }

    @Override
    public Result stopSurvey(@NotNull SurveyVo surveyVo) {
        logger.debug("stopSurvey surveyVo: {}", surveyVo);

        Optional<Survey> surveyOptional = surveyRep.findById(surveyVo.getId());
        if (!surveyOptional.isPresent()) {
            return Result.badArgumentValue();
        }
        Survey survey = surveyOptional.get();
        survey.setState(Constant.SURVEY_STOP);
        surveyRep.save(survey);
        return Result.ok();
    }

    @Override
    @Transactional
    public Result addSurvey(@NotNull AddSurveyVo addSurveyVo) {
        logger.debug("addSurvey addSurveyVo: {}", addSurveyVo);

        //验证
        if (null == addSurveyVo.getName() || addSurveyVo.getName().trim().isEmpty()) {
            return Result.badArgumentValue();
        }
        if (null == addSurveyVo.getQuestionnaireId() || addSurveyVo.getQuestionnaireId().trim().isEmpty()) {
            return Result.badArgumentValue();
        }
        Optional<Questionnaire> questionnaireOptional = questionnaireRep.findById(addSurveyVo.getQuestionnaireId());
        if (!questionnaireOptional.isPresent()) {
            return Result.badArgumentValue();
        }
        List<User> users = userRep.findByIdIn(addSurveyVo.getUserIds());
        if (null == users || users.isEmpty()) {
            return Result.badArgumentValue();
        }

        Survey survey = new Survey();
        survey.setName(addSurveyVo.getName());
        survey.setTag(addSurveyVo.getTag());
        survey.setDoneSum(0);
        survey.setRespondentSum(users.size());
        survey.setCreatedAt(new Date());
        survey.setState(Constant.SURVEY_ING);
        survey.setQuestionnaire(questionnaireOptional.get());
        List<UserSurvey> userSurveys = Lists.newLinkedList();
        for (User user : users) {
            UserSurvey userSurvey = new UserSurvey();
            userSurvey.setIsDone(false);
            userSurvey.setSurvey(survey);
            userSurvey.setUser(user);
            userSurveys.add(userSurvey);
        }
        survey = surveyRep.save(survey);
        userSurveyRep.saveAll(userSurveys);
        return Result.ok(SurveyVo.convertSurvey(survey));
    }

    @Override
    public Result countByUserId(@NotNull Map<String, Object> paramMap) {
        logger.debug("countByUserId paramMap: {}", paramMap);
        String userIdStr = "userId";
        if (((String)paramMap.get(userIdStr)).trim().isEmpty()) {
            return Result.badArgumentValue();
        }
        List<Map<String, Object>> mapList = Lists.newLinkedList();
        Map<String, Object> map1 = Maps.newLinkedHashMap();
        map1.put("isDone", true);
        map1.put("sum", userSurveyRep.countByUserIdAndIsDoneIs((String)paramMap.get(userIdStr), true));
        Map<String, Object> map2 = Maps.newLinkedHashMap();
        map2.put("isDone", false);
        map2.put("sum", userSurveyRep.countByUserIdAndIsDoneIsAndSurvey_StateIs((String)paramMap.get(userIdStr), false, Constant.SURVEY_ING));
        mapList.add(map1);
        mapList.add(map2);

        return Result.ok(mapList);
    }

    @Override
    @Transactional
    public Result userDoneSurvey(@NotNull UserSurveyVo userSurveyVo) {
        if (null == userSurveyVo.getId() || userSurveyVo.getId().isEmpty() || null == userSurveyVo.getUserSurveyResultVos()) {
            return Result.badArgumentValue();
        }
        Optional<UserSurvey> userSurveyOptional = userSurveyRep.findById(userSurveyVo.getId());
        if (!userSurveyOptional.isPresent()) {
            return Result.badArgumentValue();
        }
        UserSurvey userSurvey = userSurveyOptional.get();
        List<UserSurveyResult> userSurveyResults = Lists.newLinkedList();

        //保存结果
        for (UserSurveyResultVo userSurveyResultVo : userSurveyVo.getUserSurveyResultVos()) {
            UserSurveyResult userSurveyResult = new UserSurveyResult();
            AbstractMyBeanUtils.copyProperties(userSurveyResultVo, userSurveyResult);

            if (null == userSurveyResultVo.getQuestionVo()) {
                continue;
            }

            Optional<BaseQuestion> baseQuestionOptional = baseQuestionRep.findById(userSurveyResultVo.getQuestionVo().getId());
            if (!baseQuestionOptional.isPresent()) {
                continue;
            }

            userSurveyResult.setQuestion(baseQuestionOptional.get());
            userSurveyResult.setUserSurvey(userSurvey);
            userSurveyResults.add(userSurveyResult);
        }
        userSurveyResultRep.saveAll(userSurveyResults);

        //保存评分
        List<UserSurveyScoreVo> userSurveyScoreVos = this.calculateSurveyResultGrades(this.filterSingleChoiceQuestionUserSurveyResultVos(UserSurveyResultVo.convertUserSurveyResult(userSurveyResults)));
        List<UserSurveyScore> userSurveyScores = Lists.newLinkedList();
        for (UserSurveyScoreVo userSurveyScoreVo : userSurveyScoreVos) {
            UserSurveyScore userSurveyScore = new UserSurveyScore();
            AbstractMyBeanUtils.copyProperties(userSurveyScoreVo, userSurveyScore);
            userSurveyScore.setDimensionality(dimensionalityRep.getOne(userSurveyScoreVo.getDimensionalityVo().getId()));
            userSurveyScore.setUserSurvey(userSurvey);
            userSurveyScores.add(userSurveyScore);
        }
        userSurveyScoreRep.saveAll(userSurveyScores);

        //更新调查状态
        Survey survey = userSurvey.getSurvey();
        survey.setDoneSum(survey.getDoneSum() + 1);
        surveyRep.save(survey);

        //更新用户调查状态
        userSurvey.setIsDone(true);
        userSurvey.setDoneTime(new Date());

        return Result.ok(UserSurveyVo.convertUserSurvey(userSurveyRep.save(userSurvey)));
    }

    @Override
    public Result getUserSurvey(@NotNull String userSurveyId) {
        if (userSurveyId.trim().isEmpty()) {
            return Result.badArgumentValue();
        }
        Optional<UserSurvey> userSurveyOptional = userSurveyRep.findById(userSurveyId);
        if (userSurveyOptional.isPresent()) {
            return Result.ok(UserSurveyVo.convertUserSurvey(userSurveyOptional.get()));
        }
        return Result.badArgumentValue();
    }

    @Override
    public Result getCalculateSurveyResultGrades(UserSurveyVo userSurveyVo) {
        if (null == userSurveyVo.getId() || userSurveyVo.getId().isEmpty() || null == userSurveyVo.getUserSurveyResultVos()) {
            return Result.badArgumentValue();
        }
        Optional<UserSurvey> userSurveyOptional = userSurveyRep.findById(userSurveyVo.getId());
        if (userSurveyOptional.isPresent()) {
            List<UserSurveyScoreVo> userSurveyScoreVos =  this.calculateSurveyResultGrades(this.filterSingleChoiceQuestionUserSurveyResultVos(userSurveyVo.getUserSurveyResultVos()));
            return Result.ok(userSurveyScoreVos);
        }
        return Result.badArgumentValue();
    }

    @Override
    public ResponseEntity<byte[]> exportExcel(String surveyId) {
        if (null == surveyId) {
            return null;
        }
        Survey survey = surveyRep.getOne(surveyId);
        if (null == survey) {
            return null;
        }
        XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
        XSSFSheet sheet = xssfWorkbook.createSheet("sheet1");
        Questionnaire questionnaire = survey.getQuestionnaire();
        List<BaseQuestion> baseQuestions = baseQuestionRep.findByQuestionnaireOrderBySortAsc(questionnaire);
        List<Dimensionality> dimensionalities = dimensionalityRep.findByQuestionnaireAndParentDimensionality(questionnaire, null);
        List<Object> row = Lists.newLinkedList();

        //头
        row.add("公司");
        row.add("部门");
        row.add("员工");
        row.add("电话");
        //头的问题名
        int questionIndex = 1;
        for (BaseQuestion baseQuestion : baseQuestions) {
            StringBuilder sb = new StringBuilder("Q");
            sb.append(questionIndex++);
            sb.append("_");
            sb.append(baseQuestion.getTopic());
            row.add(sb.toString());
        }
        //头的1级维度
        for (Dimensionality dimensionality : dimensionalities) {
            row.add("D1_" + dimensionality.getName());
        }
        row.add("总分");
        ExcelUtil.sheetRowWrite(sheet, row, 0);

        //内容
        List<UserSurvey> userSurveys = survey.getUserSurveys();
        int rowIndex = 1;
        for (UserSurvey userSurvey : userSurveys) {
            row.clear();
            User user = userSurvey.getUser();
            Department department = user.getDepartment();
            row.add(DepartmentVo.getRootDepartment(department).getName());
            row.add(department.getName());
            row.add(user.getRealName());
            row.add(user.getPhone());

            //问题结果
            for (BaseQuestion baseQuestion : baseQuestions) {
                    List<UserSurveyResult> userSurveyResults = userSurveyResultRep.findByQuestionAndUserSurvey(baseQuestion, userSurvey);
                    if (null != userSurveyResults && !userSurveyResults.isEmpty()) {
                        UserSurveyResult userSurveyResult = userSurveyResults.get(0);
                        if (Constant.QUESTION_TYPE_SINGLE.equals(userSurveyResult.getType())) {
                            Choice choice = choiceRep.getOne(userSurveyResult.getAnswer());
                            if (null != choice) {
                                //有维度给分数结果，没维度给选项结果
                                if (null == baseQuestion.getDimensionality()) {
                                    row.add(choice.getDescription());
                                } else {
                                    Integer score = choice.getScore();
                                    row.add(score);
                                }
                            } else {
                                row.add(" ");
                            }
                        } else {
                            row.add(userSurveyResult.getAnswer());
                        }
                    } else {
                        row.add(" ");
                    }
            }

            //维度分数结果
            BigDecimal sum = new BigDecimal(0);
            for (Dimensionality dimensionality : dimensionalities) {
                List<UserSurveyScore> userSurveyScores = userSurveyScoreRep.findByDimensionalityAndUserSurvey(dimensionality, userSurvey);
                if (null != userSurveyScores && !userSurveyScores.isEmpty()) {
                    UserSurveyScore userSurveyScore = userSurveyScores.get(0);
                    BigDecimal score = userSurveyScore.getScore();
                    row.add(score);
                    sum = sum.add(score);
                } else {
                    row.add(" ");
                }
            }
            row.add(sum);
            logger.debug("excel write row: " + rowIndex);
            ExcelUtil.sheetRowWrite(sheet, row, rowIndex++);
        }


        return ExcelUtil.downloadExcel(xssfWorkbook, survey.getName() + "答题结果.xlsx");
    }

    /**
     * 筛选出单选题的对象集
     * @param userSurveyResultVos List<UserSurveyResultVo>
     * @return List<UserSurveyResultVo>
     */
    private List<UserSurveyResultVo> filterSingleChoiceQuestionUserSurveyResultVos(List<UserSurveyResultVo> userSurveyResultVos) {
        List<UserSurveyResultVo> dest = Lists.newLinkedList();
        if (null == userSurveyResultVos || userSurveyResultVos.isEmpty()) {
            return dest;
        }
        userSurveyResultVos.forEach(v -> {
            if (Constant.QUESTION_TYPE_SINGLE.equals(v.getType())) {
                dest.add(v);
            }
        });
        return dest;
    }

    /**
     * 计算出一级维度的得分集
     * @param userSurveyResultVos List<UserSurveyResultVo>
     * @return List<UserSurveyScoreVo>
     */
    @SuppressWarnings({"unchecked"})
    private List<UserSurveyScoreVo> calculateSurveyResultGrades(@NotNull List<UserSurveyResultVo> userSurveyResultVos) {
        //存维度id对应的得分 map
        Map<String, UserSurveyScoreVo> map = Maps.newLinkedHashMap();
        BigDecimal maxScoreSum = new BigDecimal(0);
        for (UserSurveyResultVo userSurveyResultVo : userSurveyResultVos) {
            Choice choice = choiceRep.getOne(userSurveyResultVo.getAnswer());
            if (null != choice) {
                BaseQuestion baseQuestion = choice.getBaseQuestion();
                Dimensionality dimensionality = baseQuestion.getDimensionality();
                if (null == dimensionality) {
                    continue;
                }
                Choice maxScoreChoice = choiceRep.findTopByBaseQuestionOrderByScoreDesc(baseQuestion);
                BigDecimal score = UserSurveyScoreVo.getRootScore(dimensionality, new BigDecimal(choice.getScore()));
                BigDecimal maxScore = UserSurveyScoreVo.getRootScore(dimensionality, new BigDecimal(maxScoreChoice.getScore()));
                Dimensionality rootDimensionality = DimensionalityVo.getRootDimensionality(dimensionality);
                DimensionalityVo dimensionalityVo = new DimensionalityVo();
                dimensionalityVo.setId(rootDimensionality.getId());
                dimensionalityVo.setName(rootDimensionality.getName());
                UserSurveyScoreVo userSurveyScoreVo = new UserSurveyScoreVo();
                userSurveyScoreVo.setScore(score);
                userSurveyScoreVo.setMaxScore(maxScore);
                maxScoreSum = maxScoreSum.add(maxScore);
                userSurveyScoreVo.setDimensionalityVo(dimensionalityVo);
                if (map.containsKey(rootDimensionality.getId())) {
                    userSurveyScoreVo.setScore(userSurveyScoreVo.getScore().add(map.get(rootDimensionality.getId()).getScore()));
                    userSurveyScoreVo.setMaxScore(userSurveyScoreVo.getMaxScore().add(map.get(rootDimensionality.getId()).getMaxScore()));
                    map.replace(rootDimensionality.getId(), userSurveyScoreVo);
                } else {
                    map.put(rootDimensionality.getId(), userSurveyScoreVo);
                }
            }
        }
        BigDecimal percent = new BigDecimal(100).divide(maxScoreSum, 4, BigDecimal.ROUND_HALF_UP);
        List<UserSurveyScoreVo> userSurveyScoreVos;
        Collection collection = map.values();
        if (collection instanceof List) {
            userSurveyScoreVos = (List)collection;
        } else {
            userSurveyScoreVos = Lists.newLinkedList(collection);
        }
        userSurveyScoreVos.forEach(v -> {
            v.setHundredMarkMaxScore(v.getMaxScore().multiply(percent));
            v.setHundredMarkScore(v.getScore().multiply(percent));
        });
        return userSurveyScoreVos;
    }
}
